iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 15
1

什麼是 IndexedDB ?


前面已經有提到indexedDB是一個在browser運行的Database,這裡我列出幾項主要的特性:

  • IndexedDB儲存資料的型式為「key-value pairs」。value可以是複雜的結構化object,key可以是這些object的屬性,然後我們可以依object屬性來建立索引,進行資料搜尋以及排序。
  • IndexedDB是一個基於「交易性質的資料庫模型 (transactional database model)」。任何在IndexedDB 上進行的操作都是屬於交易(transaction)。
  • 另外,交易具有生命週期,所以交易結束後又操作交易會引起例外錯誤。而且交易是自動執行生效的,無法手動執行生效。想想看如果一個使用者在兩個頁面開啟同一個網頁時,若是沒有交易機制,這兩個頁面對資料庫的存取操作將會產生衝突,因此交易機制是一個相當有用的機制。
  • IndexedDB大部分API都是「非同步」。IndexedDB API不會回傳資料,相反地,我們需要傳入回呼函數 (Callback function) 。我們並非直接、同步地儲存和取得資料,我們是請求資料庫作業,當作業結束時DOM event再通知我們作業結束,然後我們再透過事件判斷作業成功或失敗。
  • 如果把indexedDB跟傳統關聯式資料庫做個對比的話,IndexedDB中的object store就相當於Relational Database的Table。

不過在我的project中,為了避免使用callback function,我使用了第三方的套件(由jakearchibald提供),這讓我能使用promise來去操作indexedDB。(記得將code複製到project中js目錄下,並加入到pre-cache中)

IndexedDB Browser Support (96%的瀏覽器都支援,其實算相當不錯的)

接下來我就要將用戶的貼文資料(dynamic data)從原本暫存在cache,移植到indexedDB中囉!!

首先要在sw.js中,導入indexedDB的第三方套件。同時為了之後寫的程式碼能重複利用,我將針對indexedDB的操作寫在utility.js中,所以也需要導入:

importScripts('/src/js/idb.js');
importScripts('/src/js/utility.js');

來看一下在utility.js中要怎麼建立indexedDB的object store並將資料寫入:

var dbPromise = idb.open('post-store', 1, function(db) {
    if(!db.objectStoreNames.contains('posts')) {
        db.createObjectStore('posts', {keyPath: 'id'});
    }
});

function writeData(objectStore, data) {
    return dbPromise.then(function(db) {
        var tx = db.transaction(objectStore, 'readwrite');  // first step
        var store = tx.objectStore(objectStore);   // second step
        store.put(data);   // third step
        return tx.complete;
    });
}

透過第三方套件,我可以使用idb.open()來建立一個資料庫,需要輸入3個參數(1. 資料庫名稱、2. 資料庫版本[可以隨喜好設定,但不能相同]、3. callback function)。而這個callback function會以建立好的indexedDB object作為輸入,來去操作這個資料庫。

所以在callback function中,我在post-store資料庫中新增一個「名為posts的object store」,而且第二個參數是要去設定在這個object store中,我們要以哪個欄位做為key值。

接著寫要怎麼將firebase的貼文資訊寫入indexedDB呢?事實上操作indexedDB有一個標準執行流程:

  1. 開啟資料庫和交易(transaction)。
  2. 建立物件存檔(object store)。
  3. 發出資料庫操作請求,例如新增(put)或取得(get)資料。

可以看到這三個步驟就是上面writeData() function中的3 step

最後在sw.js中,將從firebase裡fetch回來的資料改成儲存到indexedDB:

// Cache then Network
self.addEventListener('fetch', function(event) {
    var url = 'https://trip-diary-f56de.firebaseio.com/posts.json';
    
    // 要修改的地方
    if(event.request.url.indexOf(url) > -1) {
        event.respondWith(
            fetch(event.request).then(function(res) {
                var clonedRes = res.clone();
                clonedRes.json().then(function(data) {
                    for(var key in data) {
                        writeData('posts', data[key]);
                    }
                });
                return res;
            })
        );
    } else if(isInArray(event.request.url, STATIC_FILES)) {
        event.respondWith(
            caches.match(event.request)
        );
    } else {
        event.respondWith(
            caches.match(event.request).then(function(response) {
                if(response) {
                    return response;
                } else {
                    return fetch(event.request).then(function(res) {
                        return caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
                            // trimCache(CACHE_DYNAMIC_NAME, 20);
                            cache.put(event.request.url, res.clone());
                            return res;
                        })
                    }).catch(function(err) {
                        return caches.open(CACHE_STATIC_NAME).then(function(cache) {
                            if(event.request.headers.get('accept').includes('text/html')) {
                                return cache.match('/offline.html');
                            }
                        });
                    });
                }
            })
        );
    }
});

說明一下,這裡我把firebase fetch回來的response,複製(clone)完後將資料轉成javascript object型式。接著用一個for loop把posts裡面的每一則貼文寫入到"posts" object store中。

來看一下畫面執行的結果吧:/images/emoticon/emoticon16.gif

發現我們已經成功地將firebase回傳dynamic data儲存至indexedDB中了,它的key就是我們當初設定的id欄位,value就是貼文的json object。

Day15 結束!! /images/emoticon/emoticon29.gif


上一篇
[Day14] 使用indexedDB暫存Dynamic Data(Part1)
下一篇
[Day16] 使用IndexedDB暫存Dynamic Data(Part3)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言